home *** CD-ROM | disk | FTP | other *** search
/ The Atari Compendium / The Atari Compendium (Toad Computers) (1994).iso / files / prgtools / gnustuff / tos / futils / futils~1 / src / misc1s.zoo / misc1 / combine / main.c < prev    next >
Encoding:
C/C++ Source or Header  |  1991-10-02  |  27.9 KB  |  1,138 lines

  1. #include <ctype.h>
  2. #include <stdio.h>
  3. #include <sys/types.h>
  4. #include <sys/stat.h>
  5. #include "util.h"
  6. #include "combine.h"
  7. /*
  8.  * main: Main program for the COMBINE utility
  9.  *
  10.  * This routine is the driver for the utility.
  11.  *
  12.  * Return value:
  13.  *      This procedure has no return value.
  14.  */
  15.  
  16. void main (argc, argv)
  17. int     argc;            /* command line argument count */
  18.  
  19. char  **argv;            /* command line arguments */
  20.  
  21. {
  22.  
  23.     struct stat     stat_buf;/* Buf. to find last written date */
  24.  
  25.     /*
  26.      * Execute program phases.
  27.      */
  28.  
  29.     if (!isatty (fileno (stdout))) {
  30.         fstat (fileno (stdout), &stat_buf);
  31.         setvbuf (stdout, mem_alloc (stat_buf.st_blksize),
  32.             _IOFBF, stat_buf.st_blksize);
  33.     }
  34.  
  35.     init (argc, argv);    /* Perform program initialization */
  36.  
  37.     if (p1_debug || pa_debug) {
  38.         fputs ("Start Pass1\n", stderr);
  39.     }
  40.     pass1 ();  /* Read files building symbol table and record arrays. */
  41.     if (p1_debug || pa_debug) {
  42.         dump_sym_tab ("Pass1 symbol table");
  43.         dump_arrays ("Pass1 arrays");
  44.     }
  45.  
  46.     if (p2_debug || pa_debug) {
  47.         fputs ("Start Pass2\n", stderr);
  48.     }
  49.     pass2 ();        /* Determine anchor points in files. */
  50.     if (p2_debug || pa_debug) {
  51.         dump_arrays ("Pass2 arrays");
  52.     }
  53.  
  54.     if (p3_debug || pa_debug) {
  55.         fputs ("Start Pass3\n", stderr);
  56.     }
  57.     pass3 ();        /* Expand anchors to non-unique lines. */
  58.     if (p3_debug || pa_debug) {
  59.         dump_arrays ("Pass3 arrays");
  60.     }
  61.  
  62.     if (p4_debug || pa_debug) {
  63.         fputs ("Start Pass4\n", stderr);
  64.     }
  65.     pass4 ();        /* Fix non-uniques surrounded by insertions */
  66.     if (p4_debug || pa_debug) {
  67.         dump_arrays ("Pass4 arrays");
  68.     }
  69.  
  70.     if (p5_debug || pa_debug) {
  71.         fputs ("Start Pass5\n", stderr);
  72.     }
  73.     pass5 ();        /* Write output files. */
  74.  
  75.     if (statistics_flag) {
  76.         dump_statistics ();
  77.     }
  78.  
  79.     if (old_new1_change_count == 0 &&
  80.             (file_count == 2 ||
  81.                 (old_new2_change_count == 0 &&
  82.                     new1_new2_change_count == 0))) {
  83.         exit (0);
  84.     } else {
  85.         exit (1);
  86.     }
  87.  
  88. }
  89. /*
  90.   * dump_arrays: dump arrays for debugging purposes
  91.   *
  92.   * This routine outputs the record arrays to the standard output file.
  93.   *
  94.   * Return value:
  95.   *      This procedure has no return value.
  96.   */
  97.  
  98. void dump_arrays (message)
  99. char   *message;        /* input */
  100.  /* Message to print before arrays */
  101.  
  102. {
  103.  
  104.     int     i;        /* Misc. variable */
  105.  
  106.     int     index;        /* Index into record array */
  107.  
  108.     int     files_left;    /* number of files left to do */
  109.  
  110.     bool file_done[MAX_FILE_COUNT];/* TRUE if EOT reached on file */
  111.  
  112.     record_type * record_ptr;/* Pointer to current record */
  113.  
  114.  
  115.  
  116.     /*
  117.      * Initialize completion parameters.
  118.      */
  119.     printf ("%s\n", message);
  120.  
  121.     files_left = file_count;
  122.     for (i = 0; i < file_count; ++i) {
  123.         file_done[i] = FALSE;
  124.     }
  125.  
  126.     /*
  127.      * For each iteration of the file read the nth record of the each file.
  128.      */
  129.     for (index = BEGIN_INDEX + 1; files_left != 0; ++index) {
  130.  
  131.         printf ("record: %5d ", index);
  132.  
  133.         /*
  134.          * Handle each file.
  135.          */
  136.  
  137.         for (i = 0; i < file_count; ++i) {
  138.  
  139.             if (!file_done[i]) {
  140.  
  141.                 if (index >= files[i].record_array_size - 1) {
  142.                     file_done[i] = TRUE;
  143.                     --files_left;
  144.                     if (files_left == 0) {
  145.                         break;
  146.                     }
  147.                     printf ("%38.38s", " ");
  148.                     continue;
  149.                 }
  150.  
  151.                 record_ptr = &(files[i].record[index]);
  152.  
  153.                 printf ("    rfa:%6d val1:%6d val2:%6d",
  154.                         record_ptr -> rfa,
  155.                         record_ptr -> value[0],
  156.                         record_ptr -> value[1]);
  157.  
  158.             } else {
  159.                 printf ("%38.38s", " ");
  160.  
  161.             }
  162.  
  163.         }
  164.  
  165.         printf ("\n");
  166.  
  167.     }
  168.  
  169. }
  170. /*
  171.  * dump_statistics: Dump statistics
  172.  *
  173.  * This routine outputs the execution statistics * to the standard output
  174.  * file.
  175.  *
  176.  * Return value:
  177.  *      This procedure has no return value.
  178.  */
  179. void dump_statistics () {
  180.  
  181.     int     i;        /* Misc. variable */
  182.  
  183.  
  184.  
  185.     /*
  186.      * Initialize completion parameters.
  187.      */
  188.  
  189.     printf ("\fStatistics:\n\n");
  190.  
  191.     printf ("Cache misses: %d\n", cache_miss);
  192.     printf ("Hash collisions: %d\n", hash_collisions);
  193.  
  194.     printf ("Line counts:\n");
  195.     for (i = 0; i < file_count; ++i) {
  196.         printf ("   %5d: '%s'\n",
  197.             files[i].record_array_size - DUMMY_RECORD_COUNT,
  198.             files[i].name_ptr);
  199.     }
  200.  
  201.     printf ("Changes:\n");
  202.     printf ("   '%s' and '%s' ", files[OLD_FILE].name_ptr,
  203.             files[NEW1_FILE].name_ptr);
  204.     if (old_new1_change_count == 0) {
  205.         printf ("are identical.\n");
  206.     } else {
  207.         printf ("have %d differences.\n", old_new1_change_count);
  208.     }
  209.  
  210.     if (file_count > 2) {
  211.         printf ("   '%s' and '%s' ", files[OLD_FILE].name_ptr,
  212.                 files[NEW2_FILE].name_ptr);
  213.         if (old_new2_change_count == 0) {
  214.             printf ("are identical.\n");
  215.         } else {
  216.             printf ("have %d differences.\n", old_new2_change_count);
  217.         }
  218.  
  219.         printf ("   '%s' and '%s' ", files[NEW1_FILE].name_ptr,
  220.                 files[NEW2_FILE].name_ptr);
  221.         if (new1_new2_change_count == 0) {
  222.             printf ("are identical.\n");
  223.         } else {
  224.             printf ("have %d differences.\n", new1_new2_change_count);
  225.         }
  226.     }
  227.  
  228. }
  229. /*
  230.  * dump_sym_tab: dump symbol table for debugging purposes
  231.  *
  232.  * This routine outputs the symbol table to the standard output file.
  233.  *
  234.  * Return value:
  235.  *      This procedure has no return value.
  236.  */
  237. void dump_sym_tab (message)
  238. char   *message;        /* input */
  239.  /* Message to print before table */
  240.  
  241. {
  242.  
  243.     int     i;        /* Misc. variable */
  244.  
  245.  
  246.  
  247.     /*
  248.      * Write each used symbol table entry.
  249.      */
  250.  
  251.     printf ("%s\n", message);
  252.  
  253.     for (i = 0; i < sym_tab_size; ++i) {
  254.         if (sym_tab_cache_ptr[i] != CACHE_FREE_ENTRY) {
  255.  
  256.             printf ("hash:%5d old:%5d new1:%5d ", i,
  257.                     files[OLD_FILE].sym_tab_index[i],
  258.                     files[NEW1_FILE].sym_tab_index[i]);
  259.  
  260.             if (file_count == 3) {
  261.                 printf ("new2:%5d ", files[NEW2_FILE].sym_tab_index[i]);
  262.             }
  263.  
  264.             if (sym_tab_cache_ptr[i] ==
  265.                 (cache_entry_type *) CACHE_NOT_IN_CACHE) {
  266.  
  267.                 printf ("(record not in cache)");
  268.             } else {
  269.                 if (sym_tab_cache_ptr[i] -> hash_code != i) {
  270.                     printf ("(cache_hash_code wrong: %d)",
  271.                         sym_tab_cache_ptr[i]->hash_code);
  272.                 }
  273.                 if (sym_tab_cache_ptr[i] -> record_length < 0) {
  274.                     sym_tab_cache_ptr[i] ->recordp[0] = '\0';
  275.                 } else {
  276.                     sym_tab_cache_ptr[i] ->
  277.                         recordp[sym_tab_cache_ptr[i] ->
  278.                         record_length] = '\0';
  279.                 }
  280.                 printf ("cache_record(%d): %s",
  281.                     sym_tab_cache_ptr[i] -> record_length,
  282.                     sym_tab_cache_ptr[i] -> recordp);
  283.             }
  284.  
  285.             printf ("\n");
  286.  
  287.         }
  288.     }
  289.  
  290. }
  291.  
  292. /*
  293.  * print_usage: Print program usage and exit with the given status
  294.  */
  295. void print_usage (status)
  296. int  status;           /* exit value */
  297. {
  298.   fputs(
  299. "\
  300. Usage: combine [-BbHhqs] [-c #,#] [-d flag] [-L #] [-P #] [-p #]\n\
  301.                [-1 text] [-2 text] old_file new1_file [new2_file]\n\
  302. Options:\n\
  303.    -H       Help option -- print this message and exit.\n\
  304.    -b       Blank compress option -- treat all whitespace as a single space.\n\
  305.    -B       Blank remove option -- ignore all whitespace.\n\
  306.    -c #,#   Column specification option -- specify column range to compare.\n\
  307.    -d flag  Debug options -- specifies how much debug information is to be\n\
  308.                output (<flag> should be one of [1-5] to debug pass #n or\n\
  309.                `a' to debug all passes).\n\
  310.    -h       h option -- produces a composite file on standard output\n\
  311.                suitable for input into combine2.\n\
  312.    -L #     Lines option -- specify the number of lines to print on a page\n\
  313.                of output. Specifying a length of zero disables pagination\n\
  314.                (default page-length is 66 lines).\n\
  315.    -P #     Prefix option -- specify the number of unchanged lines to output\n\
  316.                prior to any group of changed lines  (default is 5 lines).\n\
  317.    -p #     Postfix option -- specify the number of unchanged lines to output\n\
  318.                following any group of changed lines  (default is 5 lines).\n\
  319.    -q       Quiet option -- no output is generated if no changes are detected.\n\
  320.    -s       Statistics option -- print statistics after the comparison.\n\
  321.    -1 text  New1 file description -- symbolic description of <new1_file>.\n\
  322.    -2 text  New2 file description -- symbolic description of <new2_file>.\n\
  323. " , stderr );
  324.  
  325.   exit(status);
  326. }
  327.  
  328. /*
  329.  * init: Perform program initialization.
  330.  *
  331.  *
  332.  * This routine interprets the command line and opens the files.
  333.  *
  334.  * Return value:
  335.  *      This procedure has no return value.
  336.  */
  337.  
  338. void init (argc, argv)
  339. int     argc;            /* argument count from 'main' */
  340.  
  341. char  **argv;            /* arguments from 'main' */
  342.  
  343.  
  344. {
  345.  
  346.     char   *basename_ptr = 0;/* basename of files */
  347.  
  348.     int     cache_entry_size;/* Number of bytes in a cache entry */
  349.  
  350.     cache_entry_type * cache_ptr;/* Pointer to cache entry */
  351.  
  352.     int     different_basenames = 0;
  353.                 /* TRUE if file basenames are different */
  354.  
  355.     int     directory_count = 0;
  356.                 /* number of command line arguments which are
  357.                    actually directories */
  358.  
  359.     FILE * dummy_file;    /* can't assign to stdin on UNIX */
  360.  
  361.     long    etime;        /* Current time of day */
  362.  
  363.     int     is_directory[MAX_FILE_COUNT]; /* TRUE if file is a directory */
  364.  
  365.     int     i;        /* Misc. variable */
  366.     int     j;        /* Misc. variable */
  367.     int     k;        /* Misc. variable */
  368.  
  369.     int    max_record_len = LINE_LENGTH;    /* max initial record length */
  370.  
  371.     int     record_count;    /* Number of records in record array */
  372.  
  373.     struct stat     stat_buf;/* Buf. to find last written date */
  374.  
  375.     char   *the_cache;    /* Ptr to head of cache */
  376.  
  377.     char   *temp_ptr;    /* Misc char ptr */
  378.  
  379.     int     total_record_count;/* Total number of records in all files */
  380.  
  381.     int     c;        /* Option character */
  382.  
  383.     extern int      optind;    /* Option index */
  384.  
  385.     extern char    *optarg;    /* Option argument pointer */
  386.  
  387.     extern int      getopt ();/* getopt routine */
  388.  
  389.     extern char    *ctime ();/* convert time routine */
  390.  
  391.     extern char    *strrchr ();/* search for character in string */
  392.  
  393.  
  394. #ifdef VOS
  395.     stdout -> carriage_control = TRUE;
  396. #endif
  397.  
  398.     /*
  399.      * Scan options arguments.
  400.      */
  401.     (void) time (&etime);
  402.     (void) strcpy (exec_time, ctime (&etime));
  403.     exec_time[strlen (exec_time) - 1] = '\0'; /* remove newline character */
  404.  
  405.     for (;;) {
  406.  
  407.         c = getopt (argc, argv, "HbBhsqc:d:p:P:1:2:L:");
  408.         if (c == EOF) {
  409.             break;
  410.         }
  411.  
  412.         switch (c) {
  413.  
  414.         /*
  415.          * H option - print usage and exit
  416.          */
  417.        case 'H':
  418.             print_usage(0);
  419.             break;
  420.  
  421.         /*
  422.          * B and b option: Blank remove and blank compress
  423.          * options.
  424.          */
  425.         case 'b':
  426.             blank_compress = TRUE;
  427.             compress_records = TRUE;
  428.             break;
  429.  
  430.         case 'B':
  431.             blank_remove = TRUE;
  432.             compress_records = TRUE;
  433.             break;
  434.         /*
  435.          * c option: Compare only specified columns.
  436.          */
  437.  
  438.         case 'c':
  439.             compress_records = TRUE;
  440.             if ((column_count + 1) == (MAX_COLUMNS)) {
  441.                 error ("Too many -c options");
  442.             }
  443.  
  444.             for (j = 0; isdigit (optarg[j]); ++j) {
  445.             }
  446.             if (j == 0) {
  447.                 error ("-c option not followed by number");
  448.             }
  449.             first_column[column_count] = atoi (optarg) - 1;
  450.             /* Zero relative */
  451.  
  452.             if (first_column[column_count] < 0) {
  453.                 error ("Column specification less than column 1");
  454.             }
  455.  
  456.             if (optarg[j] != ',') {
  457.                 error ("Column specifications not separated by comma");
  458.             }
  459.  
  460.             optarg += j + 1;
  461.  
  462.             for (j = 0; isdigit (optarg[j]); ++j) {
  463.             }
  464.             if (j == 0) {
  465.                 error ("-c option not followed by two numbers");
  466.             }
  467.             last_column[column_count] = atoi (optarg) - 1;
  468.             /* Zero relative */
  469.             if (last_column[column_count] < first_column[column_count]) {
  470.                 error ("Last column spec. less then first column spec.");
  471.             }
  472.  
  473.             max_record_len = max(max_record_len,
  474.                              last_column[column_count] + 1);
  475.  
  476.             column_count++;
  477.             break;
  478.  
  479.         /*
  480.          * D option: Debug. Print debug output.
  481.          */
  482.         case 'd':
  483.             switch (*optarg) {
  484.             case 'a':
  485.                 pa_debug = TRUE;
  486.                 break;
  487.             case '1':
  488.                 p1_debug = TRUE;
  489.                 break;
  490.             case '2':
  491.                 p2_debug = TRUE;
  492.                 break;
  493.             case '3':
  494.                 p3_debug = TRUE;
  495.                 break;
  496.             case '4':
  497.                 p4_debug = TRUE;
  498.                 break;
  499.             case '5':
  500.                 p5_debug = TRUE;
  501.                 break;
  502.             default:
  503.                 error ("invalid argument following -d option");
  504.             }
  505.             break;
  506.  
  507.         /*
  508.          * h option: name of file to output HED edit file to
  509.          */
  510.         case 'h':
  511. #ifdef VOS
  512.             stdout -> carriage_control = FALSE;
  513. #endif
  514.             hed_flag = TRUE;
  515.             break;
  516.  
  517.         /*
  518.          * -P option: Number of prefix lines to output to listing file.
  519.          * -p option: Number of postfix lines to output to listing file.
  520.          */
  521.         case 'P':
  522.             prefix_lines = atoi (optarg);
  523.             if (prefix_lines > CACHE_ENTRIES - 10) {
  524.                 error ("Too many prefix lines");
  525.             }
  526.             break;
  527.  
  528.         case 'p':
  529.             postfix_lines = atoi (optarg);
  530.             break;
  531.  
  532.         /*
  533.          * -s option: Output page of statistics to stdout
  534.          */
  535.         case 's':
  536.             statistics_flag = TRUE;
  537.             break;
  538.  
  539.         /*
  540.          * -1 option: Text string to associate with 'new1' file.
  541.          * -2 option: Text string to associate with 'new2' file.
  542.          */
  543.         case '1':
  544.             files[NEW1_FILE].text_ptr = optarg;
  545.             break;
  546.  
  547.         case '2':
  548.             files[NEW2_FILE].text_ptr = optarg;
  549.             break;
  550.  
  551.         /*
  552.          * Q option: Quiet. Produce no output if no differences.
  553.          */
  554.         case 'q':
  555.             quiet_option = TRUE;
  556.             break;
  557.  
  558.         /*
  559.          * L option: specify #lines/page for output listing
  560.          */
  561.         case 'L':
  562.             page_length = atoi (optarg);
  563.             if ( page_length < 0 )
  564.                 page_length = PAGE_LENGTH;
  565.             if ( page_length  &&  (page_length - HEAD_LENGTH) < 0 )
  566.                 page_length += HEAD_LENGTH;
  567.             break;
  568.  
  569.         default:
  570.             print_usage(2);
  571.             break;
  572.         }
  573.  
  574.     }
  575.  
  576.     /*
  577.      * Handle each command line argument.
  578.      */
  579.     for (i = optind; i < argc; ++i) {
  580. #ifdef VOS
  581.         /*
  582.          * Handle redirections of 'stdin':
  583.          *
  584.          * This code won't get executed on a UNIX O.S. However,
  585.          * on VOS this code allows the same syntax to work.
  586.          */
  587.         if (argv[i][0] == '<' && argv[i][1] != '\0') {
  588.             dummy_file = freopen (&argv[i][1], "r", stdin);
  589.  
  590.             if (dummy_file == 0) {
  591.                     perror(&argv[i][1]);
  592.                 exit( 2 ) ;
  593.             }
  594.  
  595.         /*
  596.          * Handle redirections of 'stdout':
  597.          *
  598.          * This code won't get executed on a UNIX O.S. However, on VOS this
  599.          * code allows the same syntax to work.
  600.          */
  601.         } else if (argv[i][0] == '>' && argv[i][1] != '\0') {
  602.             dummy_file = freopen (&argv[i][1], "w", stdout);
  603.  
  604.             if (dummy_file == 0) {
  605.                     perror(&argv[i][1]);
  606.                     exit(2);
  607.             }
  608.  
  609.         /*
  610.          * Handle file arguments not preceeded by a specific option argument.
  611.          */
  612.         } else {
  613. #endif
  614.             if (file_count >= MAX_FILE_COUNT) {
  615.                 error ("Too many files specified");
  616.             }
  617.  
  618.             files[file_count].name_ptr = argv[i];
  619.  
  620.             stat (files[file_count].name_ptr, &stat_buf);
  621.             is_directory[file_count] =
  622.                 (stat_buf.st_mode & S_IFMT) == S_IFDIR;
  623.             if (is_directory[file_count]) {
  624.                 directory_count++;
  625.             } else {
  626.                 temp_ptr = strrchr (argv[i], '/');
  627.                 if (temp_ptr == 0) {
  628.                     temp_ptr = argv[i];
  629.                 }
  630.                 if (basename_ptr &&
  631.                     strcmp (temp_ptr, basename_ptr) != 0) {
  632.                     different_basenames = 1;
  633.                 }
  634.                 basename_ptr = temp_ptr;
  635.             }
  636.  
  637.             file_count++;
  638. #ifdef VOS
  639.         }
  640. #endif
  641.     }
  642.  
  643.     /*
  644.      * Resolve actual file names and open files.
  645.      *
  646.      * The name specified on the command line might be a directory name.
  647.      */
  648.  
  649.     if (file_count < 2) {
  650.         error ("not enough files specified");
  651.     }
  652.     if (file_count == directory_count) {
  653.         error ("cannot compare directories");
  654.     }
  655.     if (directory_count != 0 &&
  656.             file_count - directory_count > 1 &&
  657.             different_basenames) {
  658.         error ("ambiguous directory name");
  659.     }
  660.  
  661.     total_record_count = 0;
  662.     for (i = 0; i < file_count; ++i) {
  663.  
  664.         if (is_directory[i]) {
  665.             temp_ptr = mem_alloc (strlen (files[i].name_ptr) +
  666.                     strlen (basename_ptr) + 2);
  667.             sprintf (temp_ptr, "%s/%s", files[i].name_ptr,
  668.                     basename_ptr);
  669.             files[i].name_ptr = temp_ptr;
  670.         }
  671.  
  672. #ifdef VOS
  673.         files[i].seq_fd =
  674.             fopen (files[i].name_ptr, "r", max_record_len, "s", $OPEN_DB);
  675.         files[i].rnd_fd =
  676.             fopen (files[i].name_ptr, "r", max_record_len, "s", $OPEN_RMAI);
  677. #else
  678.         files[i].seq_fd = fopen (files[i].name_ptr, "r");
  679.         files[i].rnd_fd = fopen (files[i].name_ptr, "r");
  680. #endif
  681.  
  682.         if (files[i].seq_fd == 0 || files[i].rnd_fd == 0) {
  683.             perror(files[i].name_ptr);
  684.             exit(2);
  685.         }
  686.  
  687.         fstat (fileno (files[i].seq_fd), &stat_buf);
  688.  
  689.         temp_ptr = ctime (&(stat_buf.st_mtime));
  690.         temp_ptr[strlen (temp_ptr) - 1] = '\0';
  691.         files[i].lw_ptr = mem_alloc (strlen (temp_ptr) + 1);
  692.         strcpy (files[i].lw_ptr, temp_ptr);
  693.  
  694.         setvbuf (files[i].seq_fd, mem_alloc (stat_buf.st_blksize),
  695.             _IOFBF, stat_buf.st_blksize);
  696.         setvbuf (files[i].rnd_fd, mem_alloc (stat_buf.st_blksize),
  697.             _IOFBF, stat_buf.st_blksize);
  698.  
  699.         /* estimate record count by assuming 20 chars per record */
  700.         /* Don't allow overly small record counts */
  701.         record_count = max( stat_buf.st_size / 20, RA_ORIG);
  702.         files[i].record_array_alloc = record_count;
  703.         total_record_count += record_count;
  704.  
  705.         files[i].record = (record_type *)
  706.             mem_alloc (record_count * sizeof (record_type));
  707.  
  708.     }
  709.  
  710.     /*
  711.      * Sort column ranges into ascending order.
  712.      */
  713.     for (i = 0; i + 1 < column_count; ++i) {
  714.         for (j = i + 1; j < column_count; ++j) {
  715.             if (first_column[i] > first_column[j]) {
  716.                 k = first_column[i];
  717.                 first_column[i] = first_column[j];
  718.                 first_column[j] = k;
  719.                 k = last_column[i];
  720.                 last_column[i] = last_column[j];
  721.                 last_column[j] = k;
  722.             }
  723.         }
  724.     }
  725.  
  726.     /*
  727.      * Ensure there are no overlapping column ranges.
  728.      */
  729.     for (i = 0; i + 1 < column_count; ++i) {
  730.         if (last_column[i] >= first_column[i + 1]) {
  731.             error ("overlaping column ranges specified");
  732.         }
  733.     }
  734.  
  735.     /*
  736.      * Allocate cache entries.
  737.      *
  738.      * Cache entries include an extra word at the end of the buffer.
  739.      * This word allows a word of blanks to be inserted after the end
  740.      * of each read line. This, in turn, allows hash code computations
  741.      * and line comparisons to be word oriented rather than byte oriented.
  742.      *
  743.      * The cache is allocated in one chunk below for two reasons:
  744.      *    1) For small files the huge number of allocations consumes
  745.      *     significant time.
  746.      *    2) Less memory is used since mem_alloc allocates a block
  747.      *       which is larger than is actually requested. (The next larger
  748.      *       power of two.)
  749.      */
  750.     cache_entry_size =
  751.         sizeof (cache_entry_type) + sizeof (int) + max_record_len;
  752.     cache_entry_size += sizeof (int) - (cache_entry_size % sizeof (int));
  753.     the_cache = mem_alloc (CACHE_ENTRIES * cache_entry_size);
  754.     for (i = 0; i < CACHE_ENTRIES; ++i) {
  755.         cache_ptr = (cache_entry_type *) the_cache;
  756.         cache_ptr -> recordp = the_cache + sizeof(cache_entry_type);
  757.         cache_ptr -> record_alen = cache_entry_size -
  758.                     sizeof(cache_entry_type);
  759.         cache_ptr -> hash_code = HASH_FREE_ENTRY;
  760.         enq_head_dll (cache_head_ptr, cache_tail_ptr, cache_ptr,
  761.                 cache_next_ptr, cache_prev_ptr);
  762.         the_cache += cache_entry_size;
  763.     }
  764.  
  765.     /*
  766.      * Compute size of symbol table.
  767.      *
  768.      * 1) Initially quess size of symbol table as the sum of the number of
  769.      *    records in all of the input files times 2.
  770.      * 2) Never allocate a symbol table of less than 1024 entries. (This step
  771.      *    is required due to the organization of the prime number table.)
  772.      * 3) Round the size down to a multiple of 1024. (This tries to force the
  773.      *    symbol table to be an integer number of pages. It also limits the
  774.      *    size of the prime number table).
  775.      * 4) Round the size down to a prime number. (The hashing algorithm requires*
  776.      *    that the size of the table is a prime number).
  777.      */
  778.     sym_tab_size = total_record_count * 2;
  779.     sym_tab_size = max (1024, sym_tab_size);
  780.  
  781.     /* Prime number table contains only those primes which are less than
  782.        and closest to a multiple of 1024 */
  783.     for (i = 1; primes[i] != -1; ++i) {
  784.         if (sym_tab_size < primes[i]) {
  785.             break;
  786.         }
  787.     }
  788.  
  789.     sym_tab_size = primes[i - 1];
  790.  
  791.     /*
  792.      * Allocate symbol table.
  793.      */
  794.     for (i = 0; i < file_count; ++i) {
  795.         files[i].sym_tab_index = (int *) mem_alloc (sym_tab_size * sizeof (int));
  796.     }
  797.  
  798.     sym_tab_cache_ptr = (cache_entry_type **)
  799.         mem_alloc (sym_tab_size * sizeof (cache_entry_type *));
  800.  
  801. }
  802. /*
  803.  * link_records: link two records together.
  804.  *
  805.  * This routine links a record in the current file to a record in the
  806.  * corresponding file.
  807.  *
  808.  * If either of these records are already
  809.  * linked to a record in the other file, finish up all of the
  810.  * linkages. Pass5 considers it an inconsistent state if only two of
  811.  * the three linkages between files are made. Usually, this inconsistent
  812.  * state will clear itself up. However, certain input files will indeed
  813.  * allow the inconsistency to remain.
  814.  *
  815.  * Note: This routine also discovers an attempt to link records in an
  816.  * impossible fashion. Suppose, this record in the 'current' file is
  817.  * already linked to record A in the 'other' file. This record in the
  818.  * 'corresponding' file is already linked to record B in the 'other' file.
  819.  * Any attempt to link the current and corresponding records would
  820.  * require that record A and record B be the same record (impossible).
  821.  * In that circumstance, this routine acts as a no-op. The calling
  822.  * routine is not informed since this new information wouldn't change the
  823.  * decision making process which it is going through.
  824.  *
  825.  * Return value:
  826.  *      This procedure has no return value.
  827.  */
  828. void link_records (match_no, index1, index2)
  829. int     match_no;        /* input */
  830.                  /* Which relationship is being scanned */
  831.  
  832. int     index1;            /* Index into the current file of the record to
  833.                    link. */
  834.  
  835. int     index2;            /* Index into the corresponding file of the
  836.                    record to link. */
  837.  
  838. {
  839.  
  840.     file_type * file1_ptr;    /* First file - current_file */
  841.  
  842.     file_type * file2_ptr;    /* Second file - corresponding file */
  843.  
  844.     file_type * file3_ptr;    /* Third file - other file */
  845.  
  846.     int     file1_sub;    /* For each record of the first file, this is a
  847.                    subscript of the 'value' array of the
  848.                    relationship between file1 and file2 */
  849.  
  850.     int     file2_sub;    /* For each record of the second file, this is
  851.                    a subscript of the 'value' array of the
  852.                    relationship between file2 and file1 */
  853.  
  854.     int     file3_sub;    /* For each record of the third file, this is a
  855.                    subscript of the 'value' array of the
  856.                    relationship between file3 and file1 */
  857.  
  858.     int     hash_code;    /* Hash code for the record being linked. */
  859.  
  860.     int     index3;        /* Index into record array of file3 is the
  861.                    'next' record in file3 */
  862.  
  863.     int    *other_val1_ptr;    /* Pointer to the 'value' field in the record
  864.                    on file1. This is the 'value' which
  865.                    indicates the relationship to file3. */
  866.  
  867.     int    *other_val2_ptr;    /* Pointer to the 'value' field in the record
  868.                    on file2. This is the 'value' which
  869.                    indicates the relationship to file3. */
  870.  
  871.     int    *val1_ptr;    /* Pointer to the 'value' field in record on
  872.                    file1. This is the 'value' which indicates
  873.                    the relationship to file2. */
  874.  
  875.     int    *val2_ptr;    /* Pointer to the 'value' field in record on
  876.                    file2. This is the 'value' which indicates
  877.                    the relationship to file1. */
  878.  
  879.     int    *val3_ptr;    /* Pointer to the 'value' field in record on
  880.                    file3. */
  881.  
  882.  
  883.  
  884.     /*
  885.      * Set up misc local variables.
  886.      */
  887.  
  888.     if (p3_debug || p4_debug) {
  889.         printf ("link_records: matchno: %d indices: %d %d\n",
  890.                 match_no, index1, index2);
  891.     }
  892.  
  893.     file1_ptr = &files[curr_file[match_no]];
  894.     file2_ptr = &files[corres_file[match_no]];
  895.     file1_sub = value_sub[match_no];
  896.     file2_sub = rev_value_sub[match_no];
  897.  
  898.     /*
  899.      * Link the two records together.
  900.      */
  901.  
  902.     val1_ptr = &(file1_ptr -> record[index1].value[file1_sub]);
  903.     val2_ptr = &(file2_ptr -> record[index2].value[file2_sub]);
  904.  
  905.     hash_code = *val1_ptr;
  906.     *val1_ptr = index2;
  907.     *val2_ptr = index1;
  908.  
  909.     /*
  910.      * If either of these two records are already linked to the third file,
  911.      *     connect these two record to the record in the third file.
  912.      */
  913.  
  914.     other_val1_ptr =
  915.         &(file1_ptr -> record[index1].value[other_sub (file1_sub)]);
  916.     other_val2_ptr =
  917.         &(file2_ptr -> record[index2].value[other_sub (file2_sub)]);
  918.  
  919.     if (is_hash_code (*other_val1_ptr)) {
  920.         if (*other_val1_ptr != hash_code) {
  921.             error ("hash code mis-match 1");
  922.         }
  923.         if (is_hash_code (*other_val2_ptr)) {
  924.             if (*other_val2_ptr != hash_code) {
  925.                 error ("hash code mis-match 2");
  926.             }
  927.             return;
  928.         } else {
  929.             index3 = *other_val2_ptr;
  930.             *other_val1_ptr = index3;
  931.         }
  932.     } else {
  933.         index3 = *other_val1_ptr;
  934.         if (is_hash_code (*other_val2_ptr)) {
  935.             if (*other_val2_ptr != hash_code) {
  936.                 error ("hash code mis-match 3");
  937.             }
  938.             *other_val2_ptr = index3;
  939.         } else {
  940.             if (*other_val1_ptr != *other_val2_ptr) {
  941.                 /* error( "other file index mismatch 1" ) ; */
  942.                 /* In this error condition, just undo what
  943.                    we've already done */
  944.                 *val1_ptr = hash_code;
  945.                 *val2_ptr = hash_code;
  946.                 return;
  947.             }
  948.         }
  949.     }
  950.  
  951.     /*
  952.      * Connect the record in the third file to the record in the first file.
  953.      */
  954.     file3_ptr = &files[other_file[match_no]];
  955.     file3_sub = other_value_sub[match_no];
  956.     val3_ptr = &(file3_ptr -> record[index3].value[file3_sub]);
  957.  
  958.     if (is_hash_code (*val3_ptr)) {
  959.         if (*val3_ptr != hash_code) {
  960.             error ("hash code mis-match 4");
  961.         }
  962.         *val3_ptr = index1;
  963.     } else {
  964.         if (*val3_ptr != index1) {
  965.             error ("other file index mismatch 2");
  966.         }
  967.     }
  968.  
  969.     /*
  970.      * Connect the record in the third file to the record in the second file.
  971.      */
  972.  
  973.     val3_ptr =
  974.         &(file3_ptr -> record[index3].value[other_sub (file3_sub)]);
  975.  
  976.     if (is_hash_code (*val3_ptr)) {
  977.         if (*val3_ptr != hash_code) {
  978.             error ("hash code mis-match 5");
  979.         }
  980.         *val3_ptr = index2;
  981.     } else {
  982.         if (*val3_ptr != index2) {
  983.             error ("other file index mismatch 3");
  984.         }
  985.     }
  986.  
  987. }
  988. /*
  989.  * error: output fatal error message
  990.  *
  991.  * This routine outputs an error message and terminates.
  992.  *
  993.  * Return value:
  994.  *      This procedure has no return value.
  995.  */
  996.  
  997. void error (error_ptr)
  998. char   *error_ptr;        /* input */
  999.  /* Record to output. */
  1000.  
  1001. {
  1002.     fprintf (stderr, "combine: %s.\n", error_ptr);
  1003.     exit (2);
  1004. }
  1005. /*
  1006.  * mem_alloc: allocate memory
  1007.  *
  1008.  * This routine uses the standard memory allocator, heowever, if memory
  1009.  * is not available, this routine outputs an error message and terminates.
  1010.  *
  1011.  * Return value:
  1012.  *      This procedure returns a pointer to the allocated block.
  1013.  */
  1014. char   *mem_alloc (size)
  1015. int     size;            /* input */
  1016.                 /* Size (in bytes) of the block to allocate */
  1017.  
  1018. {
  1019.  
  1020.     char   *block_ptr;    /* Misc. variable */
  1021.  
  1022.     extern char    *malloc ();
  1023.  
  1024.     block_ptr = malloc (size);
  1025.     if (block_ptr == 0) {
  1026.         error ("not enough memory -- files too big");
  1027.     }
  1028.  
  1029.     return (block_ptr);
  1030.  
  1031. }
  1032.  
  1033. /*
  1034.  * reread_into_cache -- re-read a record from a file into a cache entry
  1035.  *
  1036.  * This routine is used to re-read a record (which has previously been
  1037.  * read) into a cache entry.
  1038.  */
  1039. void reread_into_cache( file_ptr, index, cache_ptr )
  1040.     file_type * file_ptr;        /* file to be read from */
  1041.     int    index;            /* record number to read */
  1042.     cache_entry_type * cache_ptr;    /* cache entry to read into */
  1043. {
  1044.     int status;
  1045.     char mbuffer[LINE_LENGTH];
  1046.  
  1047.     status = fseek (file_ptr->rnd_fd, file_ptr->record[index].rfa, 0);
  1048.     if ( status == -1 ) {
  1049.         (void) sprintf (mbuffer, "Disk error while seeking '%s'",
  1050.                 file_ptr -> name_ptr);
  1051.         error (mbuffer);
  1052.     }
  1053.  
  1054.     status = read_into_cache(file_ptr->rnd_fd,
  1055.             file_ptr->record[index].rfa,
  1056.             cache_ptr);
  1057.  
  1058.     if (status < 0) {
  1059.         (void) sprintf (mbuffer, "Disk error while re-reading '%s'",
  1060.                 file_ptr -> name_ptr);
  1061.         error (mbuffer);
  1062.     }
  1063. }
  1064.  
  1065. /*
  1066.  * read_into_cache -- read a record from a file into a cache entry
  1067.  *
  1068.  * Read a record into a cache entry. This routine reads an entire record
  1069.  * into the cache entry. If the currently allocated buffer is too small,
  1070.  * a larger buffer will be allocated.
  1071.  *
  1072.  * Return Value:
  1073.  *    Byte count read (-1 for EOF)
  1074.  */
  1075. int read_into_cache( fp, rfa, cache_ptr)
  1076.     FILE    *fp;            /* File to read */
  1077.     rfa_type    rfa;        /* rfa to read (already positioned) */
  1078.     cache_entry_type * cache_ptr;    /* cache entry to read into */
  1079. {
  1080.     char    c;
  1081.     char   *char_ptr;
  1082.     int    status;
  1083.     int     i;
  1084.  
  1085.     char_ptr = fgets (cache_ptr->recordp, cache_ptr->record_alen-sizeof(int), fp);
  1086.     if (char_ptr == NULL)
  1087.         return (-1);
  1088.  
  1089.     i = strlen (cache_ptr->recordp) - 1;
  1090.     if (cache_ptr->recordp[i] != '\n') {
  1091.         status = fseek (fp, rfa, 0);
  1092.         if ( status == -1 ) 
  1093.             error("Internal error: cannot reseek");
  1094.         for (i=0;;i++) {
  1095.             c = getc (fp);
  1096.             if (feof (fp)) {
  1097.             /* not (c==EOF) because of binary files */
  1098.                 break;
  1099.             /* This is sort of a kludge, we only check for
  1100.                non-ascii if the record length is too long */
  1101.             } else if (!isascii (c) || c == '\0' ) {
  1102.                 error ("non-ascii character in file");
  1103.             } else if (c == '\n') {
  1104.                 break;
  1105.             }
  1106.         }
  1107.         i+=2;    /* Leave room from newline and null byte */
  1108.         i+=sizeof(int); /* leave space at end for extra nulls for
  1109.                    checksum algorithm */
  1110.         i += sizeof (int) - (i % sizeof (int));
  1111.  
  1112.         /*
  1113.          * Don't deallocate the old buffer since it was probably
  1114.          * allocated as a part of a larger buffer.
  1115.          */
  1116.         cache_ptr->recordp = mem_alloc(i);
  1117.         cache_ptr->record_alen = i;
  1118.  
  1119.         status = fseek (fp, rfa, 0);
  1120.         if ( status == -1 ) 
  1121.             error("Internal error: cannot reseek");
  1122.  
  1123.         char_ptr = fgets (cache_ptr->recordp, cache_ptr->record_alen-sizeof(int), fp);
  1124.         if (char_ptr == NULL)
  1125.             return (-1);
  1126.  
  1127.         i = strlen (cache_ptr->recordp) - 1;
  1128.         /* Perhaps we should warn about this */
  1129.         if (cache_ptr->recordp[i] != '\n')
  1130.             i++;
  1131.     }
  1132.     cache_ptr->recordp[i] = '\0';
  1133.     cache_ptr->record_length = i;
  1134.  
  1135.     return (i);
  1136.  
  1137. }
  1138.